package org.yfl.mbg.enhance.plugin;

import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.GeneratedXmlFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.DefaultXmlFormatter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.*;
import org.mybatis.generator.api.dom.xml.render.DocumentRenderer;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.TableConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yfl.mbg.enhance.utils.FileUtil;

import java.util.List;
import java.util.Optional;

/**
 * mybatis组件生成插件
 * @author yanfeilin
 * @version 1.0.0
 * @date 2020/5/23 18:53
 */
public class GenMybatisClassPlugin extends PluginAdapter {

    private static final Logger LOG = LoggerFactory.getLogger(GenMybatisClassPlugin.class);

    private XmlElement selectListElement;

    private String dtoShortName;

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        //检测domain已经存在
        if(FileUtil.domainFileExists(context,introspectedTable)){
            String domainName = introspectedTable.getTableConfiguration().getDomainObjectName();
            LOG.warn("{}已经存在，不再生成!",domainName);
            return false;
        }

        //继承base domain
        String baseDomainClsFullName = context.getProperty("generator.basedomain.clsfullname");
        if(StringUtils.isNotEmpty(baseDomainClsFullName)){
            topLevelClass.addImportedType(baseDomainClsFullName);
            topLevelClass.setSuperClass(baseDomainClsFullName);
        }
        return true;
    }

    @Override
    public boolean clientGenerated(Interface interfaze, IntrospectedTable introspectedTable) {
        //判断dao文件存在
        if(FileUtil.daoFileExists(context,introspectedTable)){
            String daoFullClsName = introspectedTable.getMyBatis3JavaMapperType();
            LOG.warn("{}已经存在，不再生成!",daoFullClsName);
            return false;
        }
        return true;
    }

    private void addOtherSelectMethod(Interface mapperInterface,Method selectByPkMethod){
        Method selectListMethod = new Method("selectList");
        selectListMethod.setAbstract(true);
        selectListMethod.setVisibility(JavaVisibility.PUBLIC);
        selectListMethod.setConstructor(false);
        selectListMethod.setFinal(false);
        selectListMethod.setSynchronized(false);
        selectListMethod.setStatic(false);
        selectListMethod.setNative(false);
        FullyQualifiedJavaType returnType = FullyQualifiedJavaType.getNewListInstance();
        Optional<FullyQualifiedJavaType> modelJavaTypeOptional = selectByPkMethod.getReturnType();
        FullyQualifiedJavaType listArgType = modelJavaTypeOptional.get();
        returnType.addTypeArgument(listArgType);
        selectListMethod.setReturnType(returnType);

        String moduleName = getContext().getProperty("moduleName");
        String dtoPkg = getContextProperty("generator.dto.targetPkg");
        String dtoFullType = dtoPkg.concat(".").concat(moduleName).concat(".").concat(listArgType.getShortName())
                .concat("Dto");
        dtoShortName = listArgType.getShortName().concat("Dto");
        FullyQualifiedJavaType paramFullyJavaType = new FullyQualifiedJavaType(dtoFullType);
        Parameter selectListParam = new Parameter(paramFullyJavaType,"condition");
        selectListMethod.addParameter(selectListParam);

        mapperInterface.addImportedType(paramFullyJavaType);
        mapperInterface.addImportedType(returnType);
        mapperInterface.addMethod(selectListMethod);
    }

    private String getContextProperty(String propertyName){
        Context context = getContext();
        return context.getProperty(propertyName);
    }

    @Override
    public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        addOtherSelectMethod(interfaze,method);
        return true;
    }

    private void genSelectListElement(XmlElement selectByPkEle,IntrospectedTable introspectedTable){
        selectListElement = new XmlElement("select");
        Attribute idAttr = new Attribute("id","selectList");
        selectListElement.addAttribute(idAttr);
        selectListElement.addAttribute(new Attribute("parameterType",dtoShortName));
        selectListElement.addAttribute(new Attribute("resultMap",introspectedTable.getBaseResultMapId()));

        //修改元素
        List<VisitableElement> elementList = selectByPkEle.getElements();
        elementList.remove(elementList.size()-1);

        elementList.forEach(visitableElement -> {
            selectListElement.addElement(visitableElement);
        });

        selectListElement.addElement(new TextElement("where 1=1"));
//      <if test="accountCode != null">
//        #{accountCode,jdbcType=VARCHAR},
//      </if>
        List<IntrospectedColumn> columnList = introspectedTable.getBaseColumns();
        columnList.forEach(introspectedColumn -> {
            if(!introspectedColumn.isIdentity()){
                FullyQualifiedJavaType fieldQualifiedJavaType = introspectedColumn.getFullyQualifiedJavaType();
                String fieldName = introspectedColumn.getJavaProperty();
                String colName = introspectedColumn.getActualColumnName();
                String javaTypeShortName = fieldQualifiedJavaType.getShortName();
                switch (javaTypeShortName){
                    case "String":
                        selectAddStringCondition(fieldName,colName);
                        break;
                    case "Long":
                    case "BigDecimal":
                    case "Integer":
                        selectAddNumberCondition(fieldName,colName);
                        break;
                    case "Date":
                        selectAddDateCondition(fieldName,colName);
                        break;
                }

            }
        });
    }

    private void selectAddStringCondition(String fieldName,String columName){
        XmlElement ifElement = new XmlElement("if");
        ifElement.addAttribute(new Attribute("test",fieldName.concat(" != null and ")
                .concat(fieldName).concat(" != ''")));

        TextElement conditionElement = new TextElement("and ".concat(columName).concat(" like '%'||#{").concat(fieldName)
            .concat("}||'%'"));
        ifElement.addElement(conditionElement);
        selectListElement.addElement(ifElement);
    }

    private void selectAddNumberCondition(String fieldName,String columName){
        XmlElement ifElement = new XmlElement("if");
        ifElement.addAttribute(new Attribute("test",fieldName.concat(" != null and ")
                .concat(fieldName).concat(" != 0")));

        TextElement conditionElement = new TextElement("and ".concat(columName).concat(" = ").concat("#{").concat(fieldName)
        .concat("}"));
        ifElement.addElement(conditionElement);
        selectListElement.addElement(ifElement);

    }

    private void selectAddDateCondition(String fieldName,String columName){
        XmlElement dateStartElement = new XmlElement("if");
        dateStartElement.addAttribute(new Attribute("test",fieldName.concat("Start != null")));

        TextElement dateStartconditionElement = new TextElement("and ".concat(columName).concat(" >= ").concat("#{").concat(fieldName)
                .concat("Start}"));
        dateStartElement.addElement(dateStartconditionElement);
        selectListElement.addElement(dateStartElement);


        XmlElement dateEndElement = new XmlElement("if");
        dateEndElement.addAttribute(new Attribute("test",fieldName.concat("End != null")));

        TextElement dateEndConditionElement = new TextElement("and ".concat(columName).concat(" <![CDATA[<=]]> ").concat("#{").concat(fieldName)
                .concat("End}"));
        dateEndElement.addElement(dateEndConditionElement);
        selectListElement.addElement(dateEndElement);

    }

    @Override
    public boolean sqlMapSelectByPrimaryKeyElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        genSelectListElement(element,introspectedTable);
        return true;
    }

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        document.getRootElement().addElement(selectListElement);
        return true;
    }

    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        if (FileUtil.sqlMapXmlExists(context,introspectedTable)){
            String xmlFileName = introspectedTable.getMyBatis3XmlMapperFileName();
            LOG.warn("{}已经存在，不再生成!",xmlFileName);
            return false;
        }
        return true;
    }

    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        return false;
    }
}
